module matrix_3x3_expansion #(
    parameter
        FRAME_LINE = 16'd640,
        FRAME_ROW  = 16'd480
)(
    input                   clk             ,
    input                   rst_n           ,
    input                   img_vsync       ,
    input                   img_href        ,
    input       [15:0]      img_data        ,
    input                   data_valid      ,
    output                  img_vsync_o     ,
    output                  img_href_o      ,
    output                  data_valid_o    ,
    output      [15:0]      matrix_11       ,
    output      [15:0]      matrix_12       ,
    output      [15:0]      matrix_13       ,
    output      [15:0]      matrix_21       ,
    output      [15:0]      matrix_22       ,
    output      [15:0]      matrix_23       ,
    output      [15:0]      matrix_31       ,
    output      [15:0]      matrix_32       ,
    output      [15:0]      matrix_33        
);

// reg define
reg  [15:0] line_cnt;
reg  [15:0] row_cnt;
reg  [15:0] href_o_line_cnt;
reg  [15:0] href_o_row_cnt;
reg  [15:0] img_data_d1;
reg  [15:0] img_data_d2;
reg  [15:0] q1_d1;
reg  [15:0] q1_d2;
reg  [15:0] q1_d3;
reg  [15:0] q1_d4;
reg  [15:0] q2_d1;
reg  [15:0] q2_d2;
reg  [15:0] q2_d3;
reg  [15:0] q2_d4;
reg  [15:0] q3_d1;
reg  [15:0] q3_d2;
reg  [15:0] q3_d3;
reg  [15:0] q3_d4;
reg  [ 2:0] img_vsync_d;
reg  [ 2:0] img_href_d;
reg  [ 2:0] data_valid_d;

// wire define
wire        line_1_wr_en;
wire        line_1_rd_en;
wire        line_2_wr_en;
wire        line_2_rd_en;
wire [15:0] q1;
wire [15:0] q2;
wire [15:0] q3;

// main code-----------------------------------------------------------
// img_data_d & q1_d & q2_d
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        img_data_d1 <= 16'd0;
        img_data_d2 <= 16'd0;

        q1_d1 <= 16'd0;
        q1_d2 <= 16'd0;
        q1_d3 <= 16'd0;
        q1_d4 <= 16'd0;

        q2_d1 <= 16'd0;
        q2_d2 <= 16'd0;
        q2_d3 <= 16'd0;
        q2_d4 <= 16'd0;

        q3_d1 <= 16'd0;
        q3_d2 <= 16'd0;
        q3_d3 <= 16'd0;
        q3_d4 <= 16'd0;

        q2_d1 <= 16'd0;
        q2_d2 <= 16'd0;
    end
    else begin
        img_data_d1 <= img_data;
        img_data_d2 <= img_data_d1;   

        q1_d1 <= q1;
        q1_d2 <= q1_d1;
        q1_d3 <= q1_d2;
        q1_d4 <= q1_d3;

        q2_d1 <= q2;
        q2_d2 <= q2_d1;
        q2_d3 <= q2_d2;
        q2_d4 <= q2_d3;

        q3_d1 <= q3;
        q3_d2 <= q3_d1;
        q3_d3 <= q3_d2;
        q3_d4 <= q3_d3;
    end
end

// img_vsync_d & img_href_d
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        img_vsync_d  <= 3'd0;
        img_href_d   <= 3'd0;
        data_valid_d <= 3'd0;
    end
    else begin
        img_vsync_d  <= {img_vsync_d[1:0], img_vsync};
        img_href_d   <= {img_href_d[1:0], img_href};
        data_valid_d <= {data_valid_d[1:0], data_valid};
    end
end

// line_1_wr_en & line_2_wr_en & line_1_rd_en & line_2_rd_en
assign line_1_wr_en = (data_valid && (href_o_row_cnt <= FRAME_ROW -16'd3)) ? 1'd1 : 1'd0;
assign line_2_wr_en = ((row_cnt >= 16'd1) && data_valid && (href_o_row_cnt <= FRAME_ROW -16'd2)) ? 1'd1 : 1'd0;
assign line_1_rd_en = (data_valid && (row_cnt >= 16'd2)) ? 1'd1 : 1'd0;
assign line_2_rd_en = (data_valid && (row_cnt >= 16'd2)) ? 1'd1 : 1'd0;

// q3
assign q3 = (href_o_row_cnt >= 16'd2) ? img_data_d2 : 16'd0;

// line_cnt
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        line_cnt <= 16'd0;
    else if(line_cnt == (FRAME_LINE << 1) - 16'd1)
        line_cnt <= 16'd0;
    else if(img_href)
        line_cnt <= line_cnt + 16'd1;
    else
        line_cnt <= line_cnt;
end

// row_cnt
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        row_cnt <= 16'd0;
    else if((line_cnt == (FRAME_LINE << 1) - 16'd1) && (row_cnt == FRAME_ROW - 16'd1))
        row_cnt <= 16'd0;
    else if(line_cnt == (FRAME_LINE << 1) - 16'd1)
        row_cnt <= row_cnt + 16'd1;
    else
        row_cnt <= row_cnt;
end

// href_o_line_cnt
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        href_o_line_cnt <= 16'd0;
    else if(href_o_line_cnt == (FRAME_LINE << 1) - 16'd1)
        href_o_line_cnt <= 16'd0;
    else if(img_href_o)
        href_o_line_cnt <= href_o_line_cnt + 16'd1;
    else
        href_o_line_cnt <= href_o_line_cnt;
end

// href_o_row_cnt
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        href_o_row_cnt <= 16'd0;
    else if((href_o_line_cnt == (FRAME_LINE << 1) - 16'd1) && (href_o_row_cnt == FRAME_ROW - 16'd1))
        href_o_row_cnt <= 16'd0;
    else if(href_o_line_cnt == (FRAME_LINE << 1) - 16'd1)
        href_o_row_cnt <= href_o_row_cnt + 16'd1;
    else
        href_o_row_cnt <= href_o_row_cnt;
end

// outputs-----------------------------------------------------------------------------------------------

// img_vsync_o & img_href_o (d2)
assign img_vsync_o      = img_vsync_d[1];
assign img_href_o       = img_href_d[1];
assign data_valid_o     = data_valid_d[1];

// matrix_1x
assign matrix_11 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q1_d4 : 16'd0;
assign matrix_12 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q1_d2 : 16'd0;
assign matrix_13 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q1    : 16'd0;

// matrix_2x
assign matrix_21 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q2_d4 : 16'd0;
assign matrix_22 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q2_d2 : 16'd0;
assign matrix_23 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q2    : 16'd0;

// matrix_3x
assign matrix_31 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q3_d4 : 16'd0;
assign matrix_32 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q3_d2 : 16'd0;
assign matrix_33 = ((href_o_line_cnt >= 16'd4) && (href_o_row_cnt >= 16'd2)) ? q3    : 16'd0;

wire        full ;
wire        empty;
wire [10:0] d_cnt;
shift_fifo_2048x16_2048x16 u_shift_fifo_3 (
  .clk          (clk            ),              // input wire clk
  .srst         (~rst_n         ),              // input wire srst
  .din          (img_data       ),              // input wire [15 : 0] din
  .wr_en        (line_1_wr_en   ),              // input wire wr_en
  .rd_en        (line_1_rd_en   ),              // input wire rd_en
  .dout         (q1             ),              // output wire [15 : 0] dout
  .full         (full ),              // output wire full
  .empty        (empty),              // output wire empty
  .data_count   (d_cnt)               // output wire [10 : 0] data_count
);

shift_fifo_2048x16_2048x16 u_shift_fifo_4 (
  .clk          (clk            ),              // input wire clk
  .srst         (~rst_n         ),              // input wire srst
  .din          (img_data       ),              // input wire [15 : 0] din
  .wr_en        (line_2_wr_en   ),              // input wire wr_en
  .rd_en        (line_2_rd_en   ),              // input wire rd_en
  .dout         (q2             ),              // output wire [15 : 0] dout
  .full         (               ),              // output wire full
  .empty        (               ),              // output wire empty
  .data_count   (               )               // output wire [10 : 0] data_count
);
endmodule